package com.neusoft.databus.agent.parser;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.commons.jexl3.JexlContext;
import org.apache.commons.jexl3.MapContext;
import org.jinterop.dcom.common.JIException;
import org.jinterop.dcom.core.JIVariant;

import com.google.common.collect.Maps;
import com.neusoft.databus.agent.bean.MetricConfig;
import com.neusoft.databus.common.bean.CommandBean;
import com.neusoft.databus.common.bean.MetricBean;
import com.neusoft.databus.common.parser.Parser;
import com.neusoft.databus.common.utils.JexlMath;
import com.neusoft.databus.common.utils.JexlUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * 在OPC中，Metric实体中，command字段对应的就是OPC的item。 OPC协议不实现指令合并
 *
 * @author suncf
 */
@Slf4j
public class OpcParser implements Parser {

    @Override
    public Map<String, MetricBean> parseCollectRequest(final CommandBean commandBean) {
        // item:MetricBean
        final Map<String, MetricBean> map = Maps.newHashMap();

        commandBean.getResourceBean().stream().forEach(it -> {
            it.getMetrics().forEach(m -> {
                map.put(m.getCommand(), m);
            });
        });
        return map;
    }

    /**
     * collectedResult是Map<String,
     * Item>结构。发送的item串和返回的org.openscada.opc.lib.da.Item
     */
    @Override
    public void parseCollectResponse(final CommandBean commandBean) {

        commandBean.getResourceBean().forEach(it -> {
            final List<MetricBean> metrics = it.getMetrics();

            final JexlContext jexlContext = new MapContext();
            jexlContext.set("Math", JexlMath.getInstance());

            try {
                metrics.stream().filter(data -> data.isExecutedSuccess()).forEach(data -> {
                    final MetricConfig metricConfig = MetricConfig.create(data.getRule());
                    final JIVariant variant = (JIVariant) data.getOriginalValue();
                    try {
                        if (!commandBean.isForTest()) {
                            data.setOriginalValue(null);
                        } else {
                            data.setOriginalValue(variant.toString());
                        }
                        Object value = this.getValueFromType(variant);
                        if ((value instanceof Double) || (value instanceof Float)) {
                            final BigDecimal decimal = new BigDecimal(value.toString());
                            value = decimal.setScale(metricConfig.getPrecision(), BigDecimal.ROUND_HALF_DOWN)
                                    .doubleValue();
                        }
                        data.setValue(value);
                        data.setCollectedTime(new Date());
                    } catch (final JIException e) {
                        OpcParser.log.error(e.getMessage(), e);
                    }
                });

                metrics.stream().filter(data -> data.isParseSuccess()).forEach(data -> {
                    try {
                        final Object parsedVal = data.getValue();
                        // 数值型、字符串型、布尔型，可参与计算
                        if ((parsedVal instanceof Number) || (parsedVal instanceof String)
                                || (parsedVal instanceof Boolean)) {
                            jexlContext.set(data.getVariable(), parsedVal);
                        } else if (parsedVal instanceof Character) { // 字符型的转换成字符串后，可参与计算
                            jexlContext.set(data.getVariable(), String.valueOf(parsedVal));
                        }
                    } catch (final Exception e) {
                        OpcParser.log.error(e.getMessage(), e);
                    }
                });

                metrics.stream().filter(data -> data.isParseSuccess()).forEach(data -> {
                    try {
                        final MetricConfig metricConfig = MetricConfig.create(data.getRule());
                        if (null != metricConfig.getExpression()) {
                            Object value = JexlUtil.evaluate(metricConfig.getExpression(), jexlContext);
                            if ((value instanceof Double) || (value instanceof Float)) {
                                final BigDecimal decimal = new BigDecimal(value.toString());
                                value = decimal.setScale(metricConfig.getPrecision(), BigDecimal.ROUND_HALF_DOWN)
                                        .doubleValue();
                            }
                            data.setValue(value);
                        }
                    } catch (final Exception e) {
                        OpcParser.log.error(e.getMessage(), e);
                    }
                });
            } catch (final Exception e) {
                OpcParser.log.error(e.getMessage(), e);
            }
        });

    }

    private Object getValueFromType(final JIVariant value) throws JIException {
        Object ret = null;
        switch (value.getType()) {
        case JIVariant.VT_I1:
            ret = value.getObjectAsChar();
            break;
        case JIVariant.VT_UI1:
            ret = value.getObjectAsUnsigned().getValue().byteValue();
            break;
        case JIVariant.VT_I2:
            ret = value.getObjectAsShort();
            break;
        case JIVariant.VT_UI2:
            ret = value.getObjectAsUnsigned().getValue().shortValue();
            break;
        case JIVariant.VT_I4:
            ret = value.getObjectAsInt();
            break;
        case JIVariant.VT_UI4:
            ret = value.getObjectAsUnsigned().getValue().intValue();
            break;
        case JIVariant.VT_R4:
            ret = value.getObjectAsFloat();
            break;
        case JIVariant.VT_I8:
            ret = value.getObjectAsLong();
            break;
        case JIVariant.VT_R8:
            ret = value.getObjectAsDouble();
            break;
        case JIVariant.VT_BOOL:
            ret = value.getObjectAsBoolean();
            break;
        case JIVariant.VT_BSTR:
            ret = value.getObjectAsString2();
            break;
        case JIVariant.VT_ARRAY:
            ret = value.getObjectAsArray();
            break;
        case JIVariant.VT_DATE:
            ret = value.getObjectAsDate();
            break;
        default:
            ret = "N/A";
        }
        return ret;
    }
}
